Skip to content
On this page

Vue CLI 常规优化手段

Vue CLI 版本为 4.x

使用 Vue-cli 创建 Vue 项目,到部署阶段一些常规的优化手段汇总。

路由懒加载

把不同的路由对应的组件分割成不同的代码块,待路由请求的时候会单独打包路由,让入口文件变小,加载速度增加。

js
const routes = [
    {
        path: '/home',
        name: 'Home',
        component: () => import('@/pages/Home.vue')
    }
]
// 以函数的形式加载路由,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会加载路由组件

UI 库按需加载

按需引用 UI 库

bash
npm i babel-plugin-component -D

根目录 babel.config.js 修改

js
module.exports = {
    presets: ['@vue/cli-plugin-babel/preset'],
    plugins: [
        [
            'component',
            {
                libraryName: 'element-ui',
                styleLibraryName: 'theme-chalk',
            },
        ],
    ],
};

只引入需要组件

js
import Vue from 'vue';
import {
    Pagination,
    Input,
    Button,
} from 'element-ui';

Vue.use(Pagination);
Vue.use(Input);
Vue.use(Button);
// ...

全局引入 Less Scss

全局引入预处理 Less 或者 Scss 变量

js
pluginOptions: {
    'style-resources-loader': {
        preProcessor: 'scss',
        patterns: [
            path.resolve(__dirname, './src/styles/vars.scss')
        ]
    }
},

CDN 外链

常用第三方库可以使用 CDN 外链,不打入最终文件中

js
const PUBLIC_PATH = process.env.VUE_APP_PUBLIC_PATH;

const externals = {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    axios: 'axios',
    'babel-polyfill': 'window._babelPolyfill'
};
// CDN 外链,会插入到 index.html 中
const cdn = {
    // 开发环境
    dev: {
        css: [],
        js: []
    },
    // 生产环境
    build: {
        css: [],
        js: [
            `${PUBLIC_PATH}vendors/polyfill.min.js`,
            `${PUBLIC_PATH}vendors/vue.min.js`,
            `${PUBLIC_PATH}vendors/vue-router.min.js`,
            `${PUBLIC_PATH}vendors/axios.min.js`,
            `${PUBLIC_PATH}vendors/vuex.min.js`,
        ]
    }
};

module.exports = {
  configureWebpack: config => {
      // 为生产环境修改配置
      if (isProduction) {
          config.externals = externals;
      }
  },
  chainWebpack(config) {
    /**
     * 添加 CDN 参数到 htmlWebpackPlugin 配置中
     */
    config.plugin('html').tap(args => {
        if (isProduction) {
            args[0].cdn = cdn.build;
        } else {
            args[0].cdn = cdn.dev;
        }
        return args;
    });
  }
}

最终插入到 index.html 中

html
<head>
  <!-- 使用 CDN 的 CSS 文件 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
    <% } %>
</head>

<body>
  <!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>
</body>

代码拆分

Vue CLI 默认使用了 SplitChunksPlugin 插件来拆分代码。

js
chainWebpack(config) {
    /**
     * 添加 CDN 参数到 htmlWebpackPlugin 配置中
     */
    config.plugin('html').tap(args => {
        if (isProduction) {
            args[0].cdn = cdn.build;
        } else {
            args[0].cdn = cdn.dev;
        }
        return args;
    });
    config.when(process.env.NODE_ENV !== 'development', (config) => {
        config.optimization.splitChunks({
            chunks: 'all',
            cacheGroups: {
                libs: {
                    name: 'chunk-libs',
                    test: /[\\/]node_modules[\\/]/,
                    priority: 10,
                    chunks: 'initial',
                },
                echarts: {
                    name: 'chunk-echarts',
                    priority: 20,
                    test: /[\\/]node_modules[\\/]_?echarts(.*)/,
                },
                elementUI: {
                    name: 'chunk-elementUI',
                    priority: 30,
                    test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
                },
                commons: {
                    name: 'chunk-commons',
                    test: resolve('src/components'),
                    minChunks: 3,
                    priority: 5,
                    reuseExistingChunk: true,
                },
                styles: { //样式抽离
                    name: 'styles',
                    test: /\.(sa|sc|c)ss$/,
                    chunks: 'all',
                    enforce: true
                }
            },
        });
    });
},

动态插入首屏 Loading

添加首屏白屏动态,利用 webpack 插件 html-webpack-plugin-before-html-processing 动态写入一个 loading 状态。

根目录创建插件文件:vue-skeleton-plugin

js
const fs = require('fs');

module.exports = class VueSkeletonPlugin {
    constructor(options) {
        this.options = options;
    }

    parseSkeletonHtml() {
        // 传入 loading 文件路径
        const { skeletonPath } = this.options;
        try{
            // 解析文件
            const data = fs.readFileSync(skeletonPath, 'utf8');
            return data;
        }catch(e){
            throw new Error('读取 skeleton.html :' + JSON.stringify(e));
        }
    }

    apply(compiler) {
        const result = this.parseSkeletonHtml();
        compiler.hooks.compilation.tap(
            'VueSkeletonPlugin',
            (compilation) => {
                // 插件逻辑 调用 compilation 提供的 plugin 方法
                compilation.plugin(
                    'html-webpack-plugin-before-html-processing',
                    function (htmlPluginData) {
                        // 在 index.html 替换
                        let resultHTML = htmlPluginData.html.replace(
                            '<!-- config skeleton inset -->',
                            result
                        );
                        // 返回修改后的结果
                        htmlPluginData.html = resultHTML;
                    }
                );
            }
        );
    }
};

vue.config.js 使用本地插件

js
configureWebpack: config => {
    // ...
    config.plugins.push(
        new VueSkeletionPlugin({
            // loading 状态
            skeletonPath: resolve('public/skeletons/loading.html')
        })
    );
},

开启 Gzip

安装插件

bash
npm i compression-webpack-plugin -D

修改 vue.config.js 配置

js
const CompressionPlugin = require('compression-webpack-plugin')

module.exports = {
    configureWebpack: (config) => {
        if (process.env.NODE_ENV === 'production') {
            return {
                plugins: [new CompressionPlugin({
                    test: /.js$|.html$|.css/, //匹配文件名
                    threshold: 10240, //对超过10k的数据进行压缩
                    deleteOriginalAssets: false //是否删除原文件
                })]
            }
        }
    }    
}

服务器也要做相应配置,如果发送请求的浏览器支持 gzip,就发送给它 gzip 文件。

nginx 配置 Gzip

bash
http {
    gzip  on;
    gzip_min_length    1k;
    gzip_buffers        4 8k;
    gzip_http_version  1.0;
    gzip_comp_level    8;
    gzip_proxied        any;
    gzip_types          application/javascript text/css image/gif;
    gzip_vary          on;
    gzip_disable        "MSIE [1-6]\.";
    #以下的配置省略...
}

延申

性能优化本质上是减少首屏加载时间,让页面更快的呈现到用户面前。

优化可以从三个方面考虑:

  • 代码层

  • 网络层

  • 服务层

yh

积硅步,至千里。

公众号码不停指,欢迎关注。